package com.example.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.common.HttpStatusConstants;
import com.example.common.R;
import com.example.mapper.AddressMapper;
import com.example.mapper.AppointmentMapper;
import com.example.mapper.CommentMapper;
import com.example.mapper.ConEmployeeServiceMapper;
import com.example.mapper.ConUserAddressMapper;
import com.example.mapper.ConUserRoleMapper;
import com.example.mapper.EmployeeScoreMapper;
import com.example.mapper.FavoriteMapper;
import com.example.mapper.OrderMapper;
import com.example.mapper.RoleMapper;
import com.example.mapper.ServiceMapper;
import com.example.mapper.UserMapper;
import com.example.model.AddAddressDTO;
import com.example.model.CommentDTO;
import com.example.model.UserAddressDTO;
import com.example.model.UserRegisterDTO;
import com.example.model.entity.User;
import com.example.model.req.AddEmployeeReq;
import com.example.model.req.AdminUserLoginReq;
import com.example.model.req.UpdateFavoriteReq;
import com.example.model.req.UserLoginReq;
import com.example.model.resp.ListAdminEmployeeResp;
import com.example.model.resp.ListEmloyeeInfoResp;
import com.example.model.resp.ListEmployeeInMiniResp;
import com.example.model.resp.ListFavoriteEmployeeResp;
import com.example.model.resp.UserInfoResp;
import com.example.service.RecommendationService;
import com.example.service.UserService;
import com.example.utils.HttpUtil;
import com.example.utils.JwtUtil;
import com.example.utils.PasswordEncoder;
import com.example.utils.UserHelper;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

@Service
public class UserServiceImpl implements UserService {

    @Value("${app.miniprogram.app-id}")
    private String appId;
    @Value("${app.miniprogram.app-secret}")
    private String appSecret;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ConUserRoleMapper conUserRoleMapper;
    @Autowired
    private AddressMapper addressMapper;
    @Autowired
    private ConUserAddressMapper conUserAddressMapper;
    @Autowired
    private ConEmployeeServiceMapper conEmployeeServiceMapper;
    @Autowired
    private EmployeeScoreMapper employeeScoreMapper;
    @Autowired
    private ServiceMapper serviceMapper;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private FavoriteMapper favoriteMapper;
    @Autowired
    private AppointmentMapper appointmentMapper;
    @Autowired
    private CommentMapper commentMapper;
    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private RecommendationService recommendationService;

    @Override
    public R<String> userLogin(UserLoginReq userLoginReq) {
        // 请求地址
        String url = "https://api.weixin.qq.com/sns/jscode2session";
        // 请求参数
        String jsCode = userLoginReq.getCode();
        String grantType = "authorization_code";
        String params = String.format("appid=%s&secret=%s&js_code=%s&grant_type=%s", appId, appSecret, jsCode,
                grantType);

        String getedPhone = "";
        try {
            // 发送 GET 请求并获取响应结果
            String response = HttpUtil.sendGetRequest(url, params);
            JSONObject jsonObject = JSONUtil.parseObj(response);

            String openId = jsonObject.getStr("openid");
            String sessionKey = jsonObject.getStr("session_key");

            String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
            String accessGrantType = "client_credential";
            String accessParams = String.format("appid=%s&secret=%s&grant_type=%s", appId, appSecret,
                    accessGrantType);
            String accessTokenResponse = HttpUtil.sendGetRequest(accessTokenUrl, accessParams);
            JSONObject accessJsonObject = JSONUtil.parseObj(accessTokenResponse);
            String accessToken = accessJsonObject.getStr("access_token");

            // 请求地址
            String getPhoneUrl = String
                    .format("https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=%s", accessToken);
            // 请求头参数
            Map<String, String> heads = new HashMap<>();
            // 请求参数
            Map<String, String> getPhoneParams = new HashMap<>();
            // 数据类型
            String contentType = "application/json";
            // 连接超时
            Integer connectTimeout = 15000;
            // 读取超时
            Integer readTimeout = 60000;

            getPhoneParams.put("code", userLoginReq.getPhoneCode());
            String phoneResponse = HttpUtil.sendPostRequest(getPhoneUrl, connectTimeout, readTimeout, contentType,
                    heads,
                    getPhoneParams);
            JSONObject phoneJsonObject = JSONUtil.parseObj(phoneResponse);
            getedPhone = phoneJsonObject.getJSONObject("phone_info").getStr("phoneNumber");

            User loginUser = userMapper.getLoginTicketByPhone(getedPhone);
            if (loginUser != null) {
                if (loginUser.getPhone().equals(getedPhone)
                        && loginUser.getPassword().equals(PasswordEncoder.encode(userLoginReq.getPassword()))) {

                    if (conUserRoleMapper.getRoleByUserId(loginUser.getId()) == 1) {
                        UserHelper.setOpenId(openId);
                        UserHelper.setSessionKey(sessionKey);

                        String token = JwtUtil.generateToken(openId);
                        return R.success(token);
                    } else {
                        // 说明是员工登陆, 返回员工 id
                        String token = JwtUtil.generateTokenByUserId(loginUser.getId());
                        return R.success(token);
                    }
                } else {
                    return R.error(HttpStatusConstants.UNAUTHORIZED, "手机号或密码错误");
                }
            } else {
                // 用户注册
                UserRegisterDTO userRegisterDTO = UserRegisterDTO.builder()
                        .openId(openId)
                        .phone(getedPhone)
                        .avatar(userLoginReq.getAvatar())
                        .username(userLoginReq.getUsername())
                        .password(PasswordEncoder.encode(userLoginReq.getPassword()))
                        .build();
                userMapper.userRegister(userRegisterDTO);
                conUserRoleMapper.addUserRoleUser(userRegisterDTO.getId());
                return R.success(JwtUtil.generateToken(openId));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            UserHelper.remove();
        }
        return R.error(HttpStatusConstants.INTERNAL_SERVER_ERROR, "");
    }

    @Override
    public R<String> adminUserLogin(AdminUserLoginReq adminUserLoginReq) {
        if (adminUserLoginReq.getUsername() == null || adminUserLoginReq.getPassword() == null) {
            return R.error(HttpStatusConstants.SERVICE_UNAVAILABLE, "");
        }
        User adminUser = userMapper.getAdminLoginTicket(adminUserLoginReq.getUsername());
        if (adminUser != null) {
            String adminPassword = adminUser.getPassword();
            if (adminPassword.equals(PasswordEncoder.encode(adminUserLoginReq.getPassword()))) {
                return R.success(String.valueOf(adminUser.getId()));
            } else {
                return R.error(HttpStatusConstants.UNAUTHORIZED, "用户名或密码错误");
            }
        }
        return R.error(HttpStatusConstants.NOT_FOUND, "用户不存在");
    }

    @Override
    @Transactional
    public R<String> addEmployee(AddEmployeeReq addEmployeeReq) {
        try {
            userMapper.addEmployee(addEmployeeReq);
            Integer employeeId = addEmployeeReq.getId();
            conUserRoleMapper.addUserRoleEmployee(employeeId);

            AddAddressDTO addAddressDTO = AddAddressDTO.builder().address(addEmployeeReq.getAddress()).build();
            addressMapper.addAddress(addAddressDTO);

            conUserAddressMapper.addUserAddress(employeeId, addAddressDTO.getId());
            conEmployeeServiceMapper.addEmployeeService(employeeId, addEmployeeReq.getServiceId());
            return R.success("添加成功");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error(HttpStatusConstants.INTERNAL_SERVER_ERROR, "");
        }
    }

    @Override
    public R<List<ListEmployeeInMiniResp>> listEmployee(Integer servcieId, HttpServletRequest request) {
        String header = request.getHeader("Authorization");
        String token = header.startsWith("Bearer ") ? header.substring(7).trim() : "";

        List<Integer> employeeIds = conEmployeeServiceMapper.getEmployeeByServiceId(servcieId);
        List<ListEmployeeInMiniResp> respList = userMapper.listEmployeeByIds(employeeIds).stream()
                .map(user -> {
                    Double score = employeeScoreMapper.getAverageScoreByEmployeeId(user.getId());
                    // 如果订单还没有结束，或者今天有预约并且订单还没有完成，则没有空闲时间即 freeTime 为 false
                    boolean freeTime = (orderMapper.inProccessing(user.getId()) == 0)
                            && (appointmentMapper.haveFreeTime(user.getId()) == 0);
                    return ListEmployeeInMiniResp.builder()
                            .id(user.getId())
                            .avatar(user.getAvatar())
                            .employeeName(user.getUsername())
                            .createAt(user.getCreateAt().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli())
                            .price(user.getPrice())
                            .gender(user.isGender())
                            .score(score == null ? 0 : score)
                            .freeTime(freeTime)
                            .build();
                })
                .collect(Collectors.toList());

        if (!token.isEmpty()) {
            String openId = JwtUtil.getOpenIdFromToken(token);
            Integer userId = userMapper.getIdByOpenId(openId);
            Set<Integer> favoriteSet = new HashSet<>(favoriteMapper.getFavoriteListByUserId(userId));
            respList.forEach(resp -> {
                resp.setFavorite(favoriteSet.contains(resp.getId()));
            });

            return R.success(respList);
        } else {
            return R.success(respList);
        }
    }

    @Override
    public R<String> updateFavorite(UpdateFavoriteReq updateFavoriteReq, HttpServletRequest request) {
        String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
        Integer userId = userMapper.getIdByOpenId(openId);

        try {
            if (updateFavoriteReq.getStatus()) {
                favoriteMapper.addFavorite(updateFavoriteReq.getEmployeeId(), userId);
            } else {
                favoriteMapper.removeFavorite(updateFavoriteReq.getEmployeeId(), userId);
            }
            return R.success();
        } catch (Exception e) {
            e.printStackTrace();
            return R.error(HttpStatusConstants.INTERNAL_SERVER_ERROR, "");
        }
    }

    @Override
    public R<List<ListFavoriteEmployeeResp>> getFavoriteList(Integer userId) {
        List<Integer> employeeIds = favoriteMapper.getFavoriteListByUserId(userId);
        List<ListFavoriteEmployeeResp> respList = userMapper.listEmployeeByIds(employeeIds).stream()
                .map(employee -> {
                    String serviceName = serviceMapper.getServiceNameByUserId(employee.getId());
                    return ListFavoriteEmployeeResp.builder()
                            .employeeName(employee.getUsername())
                            .gender(employee.isGender())
                            .avatar(employee.getAvatar())
                            .id(employee.getId())
                            .serviceName(serviceName)
                            .build();
                })
                .collect(Collectors.toList());
        return R.success(respList);
    }

    @Override
    public R<UserInfoResp> userDetail(HttpServletRequest request) {
        try {
            try {
                Integer userId = JwtUtil.getUserIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
                User user = userMapper.detailByUserId(userId);

                List<UserAddressDTO> addressList = conUserAddressMapper.getAddressListByUserId(user.getId());

                Integer roleId = conUserRoleMapper.getRoleByUserId(user.getId());
                String role = roleMapper.getRoleNameById(roleId);
                UserInfoResp userInfo = UserInfoResp.builder()
                        .username(user.getUsername())
                        .gender(user.isGender())
                        .avatar(user.getAvatar())
                        .role(role)
                        .addressList(addressList)
                        .build();

                return R.success(userInfo);
            } catch (NumberFormatException e) {
                String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
                User user = userMapper.detailByOpenId(openId);

                List<UserAddressDTO> addressList = conUserAddressMapper.getAddressListByUserId(user.getId());

                Integer roleId = conUserRoleMapper.getRoleByUserId(user.getId());
                String role = roleMapper.getRoleNameById(roleId);
                UserInfoResp userInfo = UserInfoResp.builder()
                        .username(user.getUsername())
                        .gender(user.isGender())
                        .avatar(user.getAvatar())
                        .balance(user.getBalance())
                        .role(role)
                        .addressList(addressList)
                        .build();

                return R.success(userInfo);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return R.error(HttpStatusConstants.INTERNAL_SERVER_ERROR, "");
        }
    }

    @Override
    public R<String> updateAddress(HttpServletRequest request, Integer oldAddressId, Integer newAddressId) {
        String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
        if (oldAddressId == null) {
            conUserAddressMapper.initUserAddress(openId, newAddressId);
        } else {
            conUserAddressMapper.updateAddress(openId, oldAddressId, newAddressId);
        }
        return R.success();
    }

    @Override
    public R<List<UserAddressDTO>> getAddressListByUserId(HttpServletRequest request) {
        String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
        return R.success(conUserAddressMapper.getAddressListByUserId(userMapper.getIdByOpenId(openId)));
    }

    @Override
    public R<String> addNewAddress(String newAddress, HttpServletRequest request) {
        try {

            AddAddressDTO addAddressDTO = AddAddressDTO.builder().address(newAddress).build();
            addressMapper.addAddress(addAddressDTO);
            Integer addressId = addAddressDTO.getId();

            String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
            Integer userId = userMapper.getIdByOpenId(openId);
            conUserAddressMapper.addUserAddress(userId, addressId);

            return R.success();
        } catch (Exception e) {
            e.printStackTrace();
            return R.error(HttpStatusConstants.INTERNAL_SERVER_ERROR, "");
        }
    }

    @Override
    public R<String> updateAvatar(String avatar, HttpServletRequest request) {
        String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
        userMapper.updateAvatar(openId, avatar);
        return R.success();
    }

    @Override
    public R<Boolean> updateGender(boolean gender, HttpServletRequest request) {
        String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
        userMapper.updateGender(openId, gender);
        return R.success(userMapper.getGenderByOpenId(openId) == 1);
    }

    @Override
    public R<ListEmloyeeInfoResp> employeeDetail(Integer employeeId) {
        ListEmloyeeInfoResp employeeeDetail = userMapper.employeeeDetail(employeeId);
        employeeeDetail.setServiceName(serviceMapper.getServiceNameByUserId(employeeId));

        List<Integer> commentIds = employeeScoreMapper.getCommentIdsByEmployeeId(employeeId);
        if (!commentIds.isEmpty()) {
            List<CommentDTO> comments = commentMapper.getCommentsByIds(commentIds);
            employeeeDetail.setComment(comments);
        }
        return R.success(employeeeDetail);
    }

    @Override
    public R<List<ListAdminEmployeeResp>> listAdminEmployee(Integer pageSize, Integer currentPage) {
        return R.success(userMapper.listAdminEmployee((currentPage - 1) * pageSize, pageSize));
    }

    @Override
    public R<String> chargeBalance(BigDecimal chargeVal, HttpServletRequest request) {
        try {
            String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
            userMapper.chargeBalance(openId, chargeVal);
            return R.success("充值成功");
        } catch (Exception e) {
            e.printStackTrace();
            return R.error(HttpStatusConstants.INTERNAL_SERVER_ERROR, "");
        }
    }

    @Override
    public R<BigDecimal> queryBalance(HttpServletRequest request) {
        String openId = JwtUtil.getOpenIdFromToken(request.getHeader("Authorization").replace("Bearer ", ""));
        return R.success(userMapper.queryBalance(openId));
    }
}
